#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>

#include <netinet/in.h>

#include <sys/ioctl.h>


#ifndef DEBUG
#define DEBUG
#endif

typedef int s32;

#define MAX_OBJ_NAME_LEN	64
#define IFNAMSIZ		16

// s32 sockfd = -1;
static s32 true = 1;
static s32 false = 0;

typedef struct zone_oper_cmd {
	const char * cmd;
	int ( *fn )( int argc, char **argv );
} zone_cmd_t;

/*the command of get sockopt and setsockopt*/
#define ZONE_OBJ_BASE			10240
#define ZONE_OBJ_CONSTRUCT	ZONE_OBJ_BASE+1
#define ZONE_OBJ_DESTRUCT		ZONE_OBJ_CONSTRUCT+1
#define ZONE_OBJ_ADD_IF		ZONE_OBJ_DESTRUCT+1
#define ZONE_OBJ_REMOVE_IF		ZONE_OBJ_ADD_IF+1
#define ZONE_OBJ_SET_POLICY	ZONE_OBJ_REMOVE_IF+1
#define ZONE_OBJ_EMPTY			ZONE_OBJ_SET_POLICY+1
#define ZONE_OBJ_SET_MAX		ZONE_OBJ_EMPTY

#define ZONE_OBJ_EXIST			ZONE_OBJ_BASE+1
#define ZONE_OBJ_GET_IF_COUNT ZONE_OBJ_EXIST+1
#define ZONE_OBJ_SHOW			ZONE_OBJ_GET_IF_COUNT+1
#define ZONE_OBJ_GET_MAX		ZONE_OBJ_SHOW

#define ZONE_PLCY_PERMIT			0x01
#define ZONE_PLCY_DENY				0x02


/*add-if and remove-if communication data structure!*/
struct zone_if_request
{
	char zone_name[MAX_OBJ_NAME_LEN];
	char if_name[IFNAMSIZ];
};


extern int 
show_zone(const char* name,unsigned int* policy,unsigned int *layer,struct if_info** ifs,unsigned long * num_if)
{
	assert(ifs!=NULL && (*ifs)==NULL);
	char zone_name[MAX_OBJ_NAME_LEN]={0};
	strncpy(zone_name,name,MAX_OBJ_NAME_LEN);

	int sockfd=socket(PF_INET,SOCK_STREAM,0);
	if(sockfd<0){
		printf("%s %d : socket system call failed!\n",__FUNCTION__,__LINE__);
		return -errno;
	}

	int len=MAX_OBJ_NAME_LEN;
	int ret=getsockopt(sockfd,IPPROTO_IP,ZONE_OBJ_GET_IF_COUNT,
				zone_name,(socklen_t*)&len);
	if(ret!=0){
		printf("%s %d : I just can know how many interfaces does the zone(%s) owns!\n",__FUNCTION__,__LINE__,name);
		ret=-errno;
		goto free_sock;
	}

	//now len is the interface count of the zone
	int zone_len=sizeof(struct zone_show_request)+len*sizeof(struct zone_if_info);


	struct zone_show_request* result=(struct zone_show_request*)malloc(zone_len);
	if(result==NULL){
		printf("%s %d : insufficient memory!\n",__FUNCTION__,__LINE__);
		ret=-ENOMEM;
		goto free_sock;
	}
	memset(result,0,zone_len);
	strncpy(result->zone_name,zone_name,MAX_OBJ_NAME_LEN);
	result->if_count=len;

	ret=getsockopt(sockfd,IPPROTO_IP,ZONE_OBJ_SHOW,
			result,(socklen_t*)&zone_len);
	if(ret!=0){
		printf("%s %d : can't acquire a list of the interfaces that the zone(%s) owns!\n",__FUNCTION__,__LINE__,name);
		ret=-errno;
		goto free_result;
	}

	switch(result->policy){
		case NF_ACCEPT:
			*policy=ZONE_PLCY_PERMIT;
			break;			
		case NF_DROP:
			*policy=ZONE_PLCY_DENY;
			break;
		default:
			*policy=result->policy;
			printf("%s %d : invalid policy for the zone(%s)!\n",__FUNCTION__,__LINE__,name);
			break;
	}
	*layer = result->layer;
	*num_if=result->if_count;
	if(result->if_count!=0){
		(*ifs)=(struct if_info*)malloc(sizeof(struct if_info)*result->if_count);
		if((*ifs)==NULL){
			printf("%s %d : insufficient memory!\n",__FUNCTION__,__LINE__);
			ret=-ENOMEM;
			goto free_result;
		}
		memcpy((*ifs),result->ifs,sizeof(struct if_info)*result->if_count);//illegal
	}

free_result:
	free(result);
free_sock:
	close(sockfd);
	return ret;
}

static s32 do_addif_to_zone( int argc, char **argv )
{
	s32 sockfd;
	s32 retu = 1;
	struct zone_if_request request;
	

#ifdef DEBUG
	printf( "arg [ %s  %s %s].\n", argv[0], argv[1], argv[2] );
#endif

	memcpy( request.if_name, argv[1], IFNAMSIZ );
	memcpy( request.zone_name, argv[2], MAX_OBJ_NAME_LEN );

	sockfd = socket( PF_INET, SOCK_STREAM, 0 );
	if (sockfd < 0 ) {
		printf( "%s: socket system call failed.\n", __FUNCTION__ );
		return 0;
	}

#ifdef DEBUG
	printf( "sockfd [ %d ], request.if_name [ %s ], request.zone_name [ %s ].\n", sockfd, request.if_name, request.zone_name );
#endif

	retu = setsockopt( sockfd, IPPROTO_IP, ZONE_OBJ_ADD_IF, 
			&request, sizeof( struct zone_if_request ) );

	if ( retu != 0 ) {
		printf( "%s: Setsocketopt error.\n", __FUNCTION__ );
		goto close_sock;
	}

	close( sockfd );
	return 1;

close_sock:
	close( sockfd );

	return 0;
}


static s32 do_delif_from_zone( int argc, char **argv )
{
	s32 retu;
	s32 sockfd;
	struct zone_if_request request;

#ifdef DEBUG
	printf( "arg [ %s %s %s].\n", argv[0], argv[1], argv[2] );
#endif

	memcpy( request.if_name, argv[1], IFNAMSIZ );
	memcpy( request.zone_name, argv[2], MAX_OBJ_NAME_LEN );


	sockfd = socket( PF_INET, SOCK_STREAM, 0 );
	if (sockfd < 0 ) {
		printf( "%s: socket system call failed.\n", __FUNCTION__ );
		return 0;
	}

#ifdef DEBUG
	printf( "sockfd [ %d ], request.if_name [ %s ], request.zone_name [ %s ].\n", sockfd, request.if_name, request.zone_name );
#endif
	retu = setsockopt( sockfd, IPPROTO_IP, ZONE_OBJ_REMOVE_IF, 
			&request, sizeof( struct zone_if_request ) );
	if ( retu != 0 ) {
		printf( "%s: Setsocketopt erroe.\n", __FUNCTION__ );
		goto close_sock;
	}	

	close( sockfd );
	return 1;

close_sock:
	close( sockfd );

	return 0;
}

static zone_cmd_t cmds[] = {
	{ "add", 	do_addif_to_zone },
	{ "del", 	do_delif_from_zone },
	{ NULL,		NULL },
}; 

int
main( int argc, char **argv )
{
	int i;

	printf( "argc[%d].\n", argc );
	if( argc < 4 ) {
		printf( "usage..........\n" );
		return false;
	}


	for (i = 0; cmds[i].cmd != NULL; i++ ) {
		if( strcmp( cmds[i].cmd, argv[1] ) == 0 ) {
			cmds[i].fn( argc - 1, argv + 1 );
			break;
		}
	}	

	return true;
}
